#pragma once
#if (COMM_FEATURE_REGEX==1)

#include "Comm.h"
#include "Buffer.h"

#define XML_NAME_DOCUMENTNODE			"XMLDOCUMENT"
#define XML_NAME_COMMENTNODE			"XMLCOMMENT"
#define XML_NAME_TEXTNODE				"XMLTEXT"
#define XML_NAME_CDATANODE				"XMLTCDATA"

namespace COMM
{
	class XmlNode;
	class XmlDocument;
#ifdef COMM_MSVC
	template class COMM_API stdlist<XmlNode*>;
	template class COMM_API List<XmlNode*,ThreadNullLock,String>;
#endif
	typedef List<XmlNode*,ThreadNullLock,String> XmlNodeListInner;
	typedef stdlist<XmlNode*> XmlNodeList;
	class COMM_API XmlNode
	{
	public:
		enum XmlNodeType
		{
			Unknown = 0,
			// <message>
			Element = 1,
			Attribute = 2,
			Text = 3,
			// <![CDATA[my escaped text]]>
			CDATA = 4,
			// <!-- my comment -->
			Comment = 5,
			// document node
			Document = 6,
			// <?xml version='1.0'?>
			Declaration = 7,
			// </message>
			EndElement = 8
		};
	protected:
		XmlNode(XmlNodeType type,const char* name);
	public:
		virtual ~XmlNode(){}
		XmlNodeType NodeType();
		String Name();
		String Value();
		void SetValue(String value);
		XmlNodeList ChildNodes();
		bool HasChildNodes();
		XmlNode* ParentNode();
		XmlNode* GetChildNode(const String name);
		void AppendChild(XmlNode* newChild,bool atFirst=false);
		void InsertBefore(XmlNode* newChild,String name);
		bool RemoveChild(String name);
		void RemoveAll();

		String GetChildInnerText(String name);
		String InnerText();
		String InnerXml(bool format=true);
		bool HasAttribute(String name);
		String GetAttributeValue(String name);
		void SetAttribute(String name,String value);
		void RemoveAttribute(String name);
		XmlNodeList Attributes();

		// supported xpath: /bookstore/book[@lang='eng'] or //book[@lang='eng']
		XmlNode* SelectSingleNode(const char* xpath);
		XmlNodeList SelectNodes(const char* xpath);

	protected:
		friend class XmlDocument;
		XmlNodeType m_nodeType;
		String m_name;
		String m_value;
		XmlNode* m_parentNode;
		XmlNodeListInner m_childNodeList;
		XmlNodeListInner m_attributeList;
	};

	class COMM_API XmlDocument
	{
	public:
		XmlDocument();
		~XmlDocument();
		bool Load(const char* filename);
		bool LoadXml(const char* xml);
		bool Save(const char* filename,bool format=true);

		void CreateRootNode(const char* version,const char* encoding,const char* name);
		String Version();
		String Encoding();
		XmlNode* DocumentNode();
		XmlNode* RootNode();
		XmlNode* SelectSingleNode(const char* xpath);
		XmlNodeList SelectNodes(const char* xpath);

		static XmlNode* CreateNode(XmlNode::XmlNodeType type,const char* name);
		static XmlNode* CreateAttribute(const char* name);
		static XmlNode* CreateElement(const char* name);
		static XmlNode* CreateElement(const char* name,const char* text);
		static XmlNode* CreateComment(const char* data);
		static XmlNode* CreateTextNode(const char* text);
		static XmlNode* CreateCDataSection(const char* text);
		static XmlNode* CreateDeclaration(const char* version,const char* encoding);
		
	private:
		bool ReadXml(String xml);
		bool ReadElement(char* element,XmlNode** parent,XmlNode::XmlNodeType* lasttype, 
			StringList& uuids,StringList& cdatas);
	private:
		XmlNode* m_documentNode;
		XmlNode* m_declarationNode;
		XmlNode* m_rootNode;
	};

}

#endif
